<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>继承的方法</title>
</head>
<body>
	<script type="text/javascript">
	// 原型链实现继承
	function Person(name,age) {
		this.name=name;
		this.age=age;
		this.money = 6666666;
	}
	Person.prototype.show=function() {
		console.log(`我是${this.name},我今年${this.age}`)
	}
	function Worker(name, age, job) {
		// call 方法继承父类的属性
	    Person.call(this, name, age); this.job=job;
	}
	Worker.prototype.showName = function() {
		console.log(this.name)
	}
	// 如果此处有Woker的原型对象上的方法，由于原型重定向，下面的代码会覆盖此方法,例如上面声明的 showName 方法会丢失
	// 重定向原型继承父类的原型上的方法
	Worker.prototype = new Person(); // 应该写在此处
	// console.log(Worker.prototype.constructor)
	Worker.prototype.showJob=function() {
		console.log(`我的工作是${this.job}`)
	};
	var mine = new Worker('佳', 18, '写代码');
	console.log(mine);
	mine.show();
	mine.showJob();
	// mine.showName() // 这里会报错，showName is not a function
	/*
		原型重定向  Worker.prototype = new Person()
		1.声明一个构造函数式，浏览器会开辟一个内存空间，用来存储构造函数的原型对象Prototype,默认有一个constructor属性，指向构造函数。
		2.自己开辟的堆内存 Worker.prototype 没有constructor 属性，导致类的构造函数缺失（可以手动在堆内存中增加 construcotr属性）
		3.当原型重定向后，浏览器默认开辟的那个原型内存会被释放掉，如果之前已经存储了一些方法和属性，这些东西就会丢失（所以，内置类的原型不允许重定向到自己开辟的堆内存，因为内置类的原型上自带很多属性和方法，重定向后就没有了，这样是不被允许的）
	*/
	/*
		存在的问题
		1.子类可以重写父类原型上的方法 Worker.prototype.__proto__.show = null
		2.子类原型上的方法和属性在 子类原型重定向后将无法再获取到
	*/


	// 借用构造函数继承
	function Person1(name, age){
		this.name=name;
		this.age=age;
	}
	function Worker1(name, age, job){
	    Person1.call(this, name, age);
	    this.job=job;
	}
	var mine1 = new Worker1('轩', 888, '修仙的');
	console.log(mine1);
	/*
		存在的问题
		1.只能继承父类构造函数的属性
		2.无法实现构造函数的复用
		3.每个实例都有父类构造函数的副本，臃肿
	*/


	// 组合继承
	function Person2(name,age) {
		this.name=name;
		this.age=age;
	}
	Person2.prototype.show=function() {
	    console.log(`我是${this.name},我今年${this.age}`)
	}
	function Worker2(name, age, job) {
	    Person2.call(this, name, age); // 继承父类构造函数上的属性
	    this.job=job;
	}
	Worker2.prototype = new Person2(); // 继承父类原型上的属性和方法
	Worker2.prototype.constructor = Worker2; // 手动通过 constructor 属性指向子类构造函数
	Worker2.prototype.showJob=function (){
	    console.log(`我的工作是${this.job}`) 
	};
	var mine2 = new Worker2('伟', 666, '摸鱼的');
	console.log(mine2);
	mine2.show();
	mine2.showJob();
	/*
		存在的问题
		调用了两次父类的构造函数（消耗内存）
		再重写子类的原型对象后，子类的原型的 constructor 指向父类的构造函数
		手动改写 constructor 属性，子类的构造函数会替代原型上的父类的构造函数
	*/


	// 原型式继承
	function Worker3(o) {
		function Empty(){
			this.dream = '无所不能'
		}
		Empty.prototype = o;
		return new Empty()
	}
	var obj = {
		name: '伟',
		age: '30',
		job: 'codeing code'
	}
	var obj1 = Worker3(obj)
	var obj2 = Object.create({})
	console.log(obj1,obj2)
	/*
		重点：
		1.用一个函数包装一个对象，然后返回这个函数的调用，这个函数就变成了可以随意添加属性的实例或对象
		Object.create()就是这个原理
		特点：
		类似于复制一个对象，用函数来包装
		缺点：
		1.所有实例都会继承原型上的属性
		2.无法实现复用。新实例属性都是后边添加的

		Object.create()
		1.创建一个空对象
		2.让新创建的空对象的__proto__属性指向第一个传进来的对象（把传入的空对象作为新创建对象的原型）
	*/


	// 寄生式继承
	function createAnother(o) {
		var person = Worker3(o);
	    person.show = function() {
	        console.log(`我是${this.name}`)
	    }
	    return person;
	}
	var people = {
	    name: 'jiajia',
	    age: 18,
	    job: '打杂的'
	};
	var anotherMine = createAnother(people);
	anotherMine.show()
	console.log(anotherMine)
	/*
		重点：
		就是给原型继承外边套了个壳子，也可以用Object.create() 方法实现
		优点：
		没有创建自定义类型，因为只是套了个壳子返回对象（这个），这个函数顺理成章就成了创建的新对象。
		缺点：
		没用到原型，无法实现复用
	*/


	// 寄生组合式继承
	function inheritProto(parent, child) {
		const o = Object.create(parent.prototype);
		o.constructor = child;
		child.prototype = o;
	}
	// 父类构造函数
	function Parent(Pname) {
		this.Pname = Pname
	}
	Parent.prototype.getPname = function() {
		console.log(this.Pname)
	}
	// 子类构造函数
	function Child(Pname, age) {
		Parent.call(this, Pname);
		this.age = age
	}
	inheritProto(Parent, Child)
	Child.prototype.getAge = function() {
		console.log(this.age)
	}
	/*
		寄生组合式继承，集寄生式继承和组合继承的优点与一身，是实现基于类型继承的最有效方式
	*/


	// ES6 class 类实现继承
	class PersonObj {
		constructor(name, age) {
			this.name = name;
			this.age = age;
		}
		show() {
			console.log(this.name,this.age)
		}
	}

	class ChildPerson extends PersonObj {
		constructor(name, age, job) {
			super(name, age);
			this.job = job;
		}
		showJop() {
			console.log(this.job)
		}
	}
	const o1 =  new ChildPerson('zxw','18','修仙')
	o1.show()
	o1.showJop()
	</script>
</body>
</html>